package com.smart4s.app.core.positionservice.service;

import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.util.Log;


import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;

import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.smart4s.app.core.positionservice.utils.ConfigManager;
import com.smart4s.app.core.positionservice.utils.FileUtils;
import com.smart4s.app.core.positionservice.R;
import com.smart4s.app.core.positionservice.activity.MainActivity;
import com.smart4s.app.core.positionservice.entity.MapParams;
import com.smart4s.app.core.positionservice.utils.JSONUtils;
import com.ubeacon.eg_led_decoder.EgLedDecoder;
import com.ubeacon.eg_led_decoder.beans.Custom;
import com.ubeacon.eg_led_decoder.beans.Datum;
import com.ubeacon.eg_led_decoder.beans.DecodeParams;
import com.ubeacon.eg_led_decoder.beans.DeviceModel;
import com.ubeacon.eg_led_decoder.beans.Light;
import com.ubeacon.eg_led_decoder.beans.LightInfo;
import com.ubeacon.eg_led_decoder.camera.BaseCameraOption;
import com.ubeacon.eg_led_decoder.camera.LCameraOption;
import com.ubeacon.eg_led_decoder.utils.PointD;

import org.greenrobot.eventbus.EventBus;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

import static android.hardware.camera2.CameraMetadata.LENS_FACING_BACK;

/**
 * 光定位服务
 *
 * @author yanxiaoqian
 */
public class LightDecoderService extends Service implements BaseCameraOption.OnDecodeLocationListener {

    private static final String CHANNEL_ID_STRING = "1";
    private String TAG = "LightDecoderService";
    //摄像头
    private static final int CAMERA_FACING = LENS_FACING_BACK;
    //视觉定位返回
    public static final String SIGHT_LID = "0x00000001";
    //默认楼层
    public static final int DEFAULT_GROUP_ID = 1;
    //光定位摄像头
    private LCameraOption mCamera;
    //地图配置参数
    private MapParams mMapParams;
    //基准点
    private PointD mOriginPoint = new PointD();
    private Handler mHandler = new Handler();

    @Override
    public void onCreate() {
        super.onCreate();

        decoderConfig();
        // 注意，如果需要对新灯解码则不能设置灯数据，
        // 因为sdk解灯后判断如果有设置灯数据，
        // 则判断解出来的灯是否在灯数据里面存在，由于是新灯而因此被过滤无法输出
        // initData();
        initCamera();
        //持续发送位置信息广播
        initTime();


        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
            NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
            NotificationChannel channel = null;
            channel = new NotificationChannel(CHANNEL_ID_STRING, getString(R.string.app_name), NotificationManager.IMPORTANCE_LOW);
            notificationManager.createNotificationChannel(channel);
            Notification notification = new Notification.Builder(getApplicationContext(), CHANNEL_ID_STRING).build();
            startForeground(1, notification);
        }
        mHandler.postDelayed(new Runnable() {
            @Override
            public void run() {
                stopForeground(true);
            }
        }, 1000);

    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        // 释放相机，停止光解码
        pauseCamera();

        Intent localIntent = new Intent();
        localIntent.setClass(this, LightDecoderService.class); //销毁时重新启动Service
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            this.startForegroundService(localIntent);
        }
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public boolean onUnbind(Intent intent) {
        // 释放相机，停止光解码
        pauseCamera();
        return super.onUnbind(intent);
    }

    private void pauseCamera() {
        // 释放相机，停止光解码
        try {
            mCamera.pause();
        } catch (Exception ignore) {
        }
    }

    /**
     * 初始化光定位参数
     */
    private void decoderConfig() {
        DecoderConfig decoderConfig = new DecoderConfig();
        // 光定位参数，硬件更新需要更新
        decoderConfig.initHW(this, android.os.Build.MODEL, "front", "50", "1280x720",
                ConfigManager.getFilePath(ConfigManager.DECODER_HW_PARAMS_JSON));
        // 光定位自定义参数
        decoderConfig.initCustomParams(this,
                ConfigManager.getFilePath(ConfigManager.DECODER_CUSTOM_PARAMS_JSON));
        // 地图配置文件
        // 解析地图配置文件
        mMapParams = FileUtils.convertJsonFileToBean(ConfigManager.getFilePath(ConfigManager.MAP_PARAMS_JSON),
                new TypeToken<MapParams>() {
                });
    }

    /**
     * 如果做定位需要设置灯数据进去，如果仅仅识别灯具采集位置信息则不需要
     * <p>
     * 由于蜂鸟地图采集的单位是 米，而 sdk 中需要的数据单位是 厘米，因此设置数据时需要转换，返回数据时重新转换回与地图一样的单位 米
     */
    private void initData() {
        File file = new File(ConfigManager.getFilePath(ConfigManager.LIGHT_INFO_JSON));
        // 未录制灯数据，则先录制灯数据
        if (!file.exists()) return;

        // 灯配置文件
        ArrayList<Light> lightList = FileUtils.convertJsonFileToBean(
                ConfigManager.getFilePath(ConfigManager.LIGHT_INFO_JSON),
                new TypeToken<ArrayList<Light>>() {
                });

        double minY;
        double minX;
        ArrayList<Double> xPoints = new ArrayList<>();
        ArrayList<Double> yPoints = new ArrayList<>();

        for (Light light : lightList) {
            double X = (new BigDecimal(light.getX())).movePointRight(2).doubleValue();
            double Y = (new BigDecimal(light.getY())).movePointRight(2).doubleValue();
            xPoints.add(X);
            yPoints.add(Y);
        }

        minX = Math.floor(Collections.min(xPoints));
        minY = Math.floor(Collections.min(yPoints));
        mOriginPoint.x = minX;
        mOriginPoint.y = minY;

        // 偏移处理
        for (Light light : lightList) {
            light.setX(String.valueOf(Double.parseDouble(light.getX()) * 100.0D - mOriginPoint.getX()));
            light.setY(String.valueOf(Double.parseDouble(light.getY()) * 100.0D - mOriginPoint.getY()));
        }

        // 设置灯数据信息
        for (Light light : lightList) {
            EgLedDecoder.setLeds(light.toArray(), 1);
        }
    }

    /**
     * 初始化相机
     */
    private void initCamera() {
        float azimuthoOffset = mMapParams.getOffsetToNorth();
        int iso = mMapParams.getIso();
        mCamera = new LCameraOption(this);
        // 设置前后摄像头
        mCamera.setCameraFacing(CAMERA_FACING);
        // 指针偏转，根据环境进行调节
        mCamera.setAzimuthOffset(azimuthoOffset);
        // 曝光度（ISO），根据环境调节
        mCamera.setSensorSensitivity(iso);
        // 设置监听
        mCamera.setOnDecodeLocationListener(this);
        // 开始
        try {
            // 捕获相机检测时产生的异常
            mCamera.start();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * @param lightId 解码回调参数，数组长度 为11
     */
    @Override
    public void onGetPid(double[] lightId) {
        if ((long) lightId[1] == 0) {
            // 0 为定位不到数据， 返回
            return;
        }

        LightLocationResult lightLocationResult = new LightLocationResult();
        lightLocationResult.count = (long) lightId[0];
        lightLocationResult.lid = String.format("0x%08X", BigDecimal.valueOf(lightId[1]).toBigInteger());
        lightLocationResult.floor = DEFAULT_GROUP_ID;
        Log.e("lightLocationResult.lid", lightLocationResult.lid + "");
        Log.e("lightLocation.floor", lightLocationResult.floor + "");
        /**
         * 如下数据如果未设置灯数据的，则这里返回的位置信息是无效的
         */
        // 数据恢复偏移
        lightLocationResult.x = lightId[2] + mOriginPoint.getX();
        lightLocationResult.y = lightId[3] + mOriginPoint.getY();

        // 数据转换，cm 到 m
        lightLocationResult.x = BigDecimal.valueOf(lightLocationResult.x).movePointLeft(2).doubleValue();
        lightLocationResult.y = BigDecimal.valueOf(lightLocationResult.y).movePointLeft(2).doubleValue();

        // 指针偏移
        lightLocationResult.mobileAzi = (float) lightId[4];

        lightLocationResult.Mx_center = lightId[5];
        lightLocationResult.My_center = lightId[6];
        lightLocationResult.M_theta = lightId[7];
        lightLocationResult.Mx_distance = lightId[8];
        lightLocationResult.My_distance = lightId[9];
        lightLocationResult.flag = (int) lightId[10];

        // 通知观察者
        if (EventBus.getDefault().hasSubscriberForEvent(LightLocationResult.class)) {
            EventBus.getDefault().post(lightLocationResult);
        }
    }

    /**
     * 光定位结果
     */
    public static class LightLocationResult extends LightInfo {
        //楼层信息
        public static int floor;
        //磁力器校准成功标识，1 成功，其他失败
        public int flag;

        public int getFloor() {
            return floor;
        }

        @Override
        public String toString() {
            String string = super.toString();
            return string
                    + "\n" + String.format("%14s", "floor:" + floor)
                    + "\n" + String.format("%14s", "flag:" + flag);
        }
    }

    //该类从解码库中拷贝出来做修改
    private class DecoderConfig {
        private JsonReader reader;
        private InputStream in;
        private final String TAG = "DecoderConfig";
        private ArrayList<Light> lightList = new ArrayList();

        public DecoderConfig() {
        }

        //修改部分：将原来从 assets 读取改为文件读取
        @Nullable
        private JsonReader getReader(Context context, String filename) {
            if (filename == null) {
                Log.e("DecoderConfig", "getReader: file is null!");
                return null;
            } else {
                FileInputStream fileInputStream = null;
                try {
                    fileInputStream = new FileInputStream(filename);
                    this.reader = new JsonReader(new InputStreamReader(fileInputStream, "UTF-8"));
                } catch (IOException var5) {
                    var5.printStackTrace();
                    if (fileInputStream != null) {
                        try {
                            fileInputStream.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                    Log.e("DecoderConfig", "getReader: File Open Fails");
                }

                return this.reader;
            }
        }

        private void defaultHWconfig() {
            EgLedDecoder.init("LIB_LED_DECODE_VERSION", "ENCODE_14_7x8_50_1");
            EgLedDecoder.init("CAMERA_FOCAL_X_LENGHT", "1231.8");
            EgLedDecoder.init("CAMERA_FOCAL_Y_LENGHT", "1231.8");
            EgLedDecoder.init("CAMERA_CENTER_X", "640");
            EgLedDecoder.init("CAMERA_CENTER_Y", "370");
            EgLedDecoder.init("CAMERA_BAND_MIN", "1.5");
            EgLedDecoder.init("CAMERA_BAND_MAX", "2.5");
            EgLedDecoder.init("CAMERA_BAND_FIX", "1.922");
        }

        public boolean initHW(Context context, String deviceModel, String facing, String period, String previewSize, String jsonFileName) {
            this.reader = this.getReader(context, jsonFileName);
            if (this.reader == null) {
                return false;
            } else {
                Gson gson = new Gson();
                Type listType = (new TypeToken<ArrayList<DeviceModel>>() {
                }).getType();
                ArrayList<DeviceModel> deviceModels = (ArrayList) gson.fromJson(this.reader, listType);
                if (this.reader != null) {
                    try {
                        this.reader.close();
                    } catch (IOException var16) {
                        var16.printStackTrace();
                    }
                }

                if (deviceModels != null && !deviceModels.isEmpty()) {
                    Iterator var10 = deviceModels.iterator();

                    label74:
                    while (true) {
                        DeviceModel dm;
                        do {
                            if (!var10.hasNext()) {
                                break label74;
                            }
                            dm = (DeviceModel) var10.next();
                        } while (!dm.getDevice().equals(deviceModel));

                        List<Datum> datas = dm.getData();
                        if (datas == null || datas.isEmpty()) {
                            this.defaultHWconfig();
                            return false;
                        }

                        Iterator var13 = datas.iterator();

                        while (var13.hasNext()) {
                            Datum data = (Datum) var13.next();
                            if (data.getCameraFacing() == null || data.getImageResolution() == null || data.getLibLedDecodeVersion() == null || data.getLightPeriod() == null || !data.getCameraFacing().equals(facing) || !data.getImageResolution().equals(previewSize) || !data.getLightPeriod().equals(period)) {
                                this.defaultHWconfig();
                                return false;
                            }

                            EgLedDecoder.init("LIB_LED_DECODE_VERSION", data.getLibLedDecodeVersion());
                            DecodeParams decodeParams = data.getDecodeParams();
                            if (decodeParams != null) {
                                EgLedDecoder.init("CAMERA_FOCAL_X_LENGHT", decodeParams.getCameraFocalXLenght());
                                EgLedDecoder.init("CAMERA_FOCAL_Y_LENGHT", decodeParams.getCameraFocalYLenght());
                                EgLedDecoder.init("CAMERA_CENTER_X", decodeParams.getCameraCenterX());
                                EgLedDecoder.init("CAMERA_CENTER_Y", decodeParams.getCameraCenterY());
                                EgLedDecoder.init("CAMERA_BAND_MIN", decodeParams.getCameraBandMin());
                                EgLedDecoder.init("CAMERA_BAND_MAX", decodeParams.getCameraBandMax());
                                EgLedDecoder.init("CAMERA_BAND_FIX", decodeParams.getCameraBandFix());
                                return true;
                            }
                        }
                    }
                }

                this.defaultHWconfig();
                Log.e("DecoderConfig", "Error configuring decoder hardware parameters.");
                return false;
            }
        }

        public boolean initCustomParams(Context context, String filename) {
            this.reader = this.getReader(context, filename);
            if (this.reader == null) {
                return false;
            } else {
                Gson gson = new Gson();
                Type type = (new TypeToken<Custom>() {
                }).getType();
                Custom param = (Custom) gson.fromJson(this.reader, type);
                if (this.reader != null) {
                    try {
                        this.reader.close();
                    } catch (IOException var7) {
                        var7.printStackTrace();
                    }
                }

                if (param != null) {
                    EgLedDecoder.init("ROOM_CEILING_HEIGHT", param.getRoomCeilingHeight());
                    EgLedDecoder.init("MOBILE_DEVICE_HEIGHT", param.getMobileDeviceHeight());
                    EgLedDecoder.init("LIGHT_SEARCH_RADIUS", param.getLightSearchRadius());
                    EgLedDecoder.init("LIGHT_NONE_PERIOD", param.getLightNonePeriod());
                    EgLedDecoder.init("LIGHT_ID_NMATCH", param.getLightIdNmatch());
                    EgLedDecoder.init("NAV_PACE_LENGTH", param.getNavPaceLength());
                    EgLedDecoder.init("NAV_PACE_SENSITIVITY", param.getNavPaceSensitivity());
                    EgLedDecoder.init("NAV_PACE_MIN_RADIUS", param.getNavPaceMinRadius());
                    EgLedDecoder.init("NAV_PACE_CLEAR_COUNT", param.getNavPaceClearCount());
                    EgLedDecoder.init("NAV_PACE_SIGMA_Q", param.getNavPaceSigmaQ());
                    EgLedDecoder.init("NAV_PACE_SIGMA_R", param.getNavPaceSigmaR());
                    return true;
                } else {
                    return false;
                }
            }
        }
    }

    //防止Service被Kill
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // TODO Auto-generated method stub
        Log.v("TrafficService", "startCommand");
        //flags = START_STICKY;
        //return super.onStartCommand(intent, flags, startId);
        return START_REDELIVER_INTENT;
    }

    private void initTime() {
        Timer timer = new Timer(true);
        TimerTask timerTask = new TimerTask() {
            @Override
            public void run() {
                // 通过Intent类的构造方法指定广播的ID
                Intent intent1 = new Intent("com.smart4s.app.core.positionservice.action.POSITIONINFO");
                // 将要广播的数据添加到Intent对象中
                intent1.putExtra("text", JSONUtils.getInstance().changeArrayDateToJson());
                intent1.setPackage("com.smart4s.app.core.iotservice");
                // 发送广播
                sendBroadcast(intent1);
                Log.e("MyService+To", intent1.getAction());
                Log.e("MyService", JSONUtils.getInstance().changeArrayDateToJson().toString());
            }
        };
        timer.schedule(timerTask, 0, 10000);
    }

}
